// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/webui/i18n_source_stream.h"

#include <algorithm>
#include <utility>

#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "net/base/io_buffer.h"

namespace content {

I18nSourceStream::~I18nSourceStream() { }

std::unique_ptr<I18nSourceStream> I18nSourceStream::Create(
    std::unique_ptr<SourceStream> upstream,
    SourceStream::SourceType type,
    const ui::TemplateReplacements* replacements)
{
    DCHECK(replacements);
    std::unique_ptr<I18nSourceStream> source(
        new I18nSourceStream(std::move(upstream), type, replacements));
    return source;
}

I18nSourceStream::I18nSourceStream(std::unique_ptr<SourceStream> upstream,
    SourceStream::SourceType type,
    const ui::TemplateReplacements* replacements)
    : FilterSourceStream(type, std::move(upstream))
    , replacements_(replacements)
{
}

std::string I18nSourceStream::GetTypeAsString() const
{
    return "i18n";
}

int I18nSourceStream::FilterData(net::IOBuffer* output_buffer,
    int output_buffer_size,
    net::IOBuffer* input_buffer,
    int input_buffer_size,
    int* consumed_bytes,
    bool upstream_end_reached)
{
    // |input_| is often empty (or it may have something from the prior call).
    input_.append(input_buffer->data(), input_buffer_size);
    *consumed_bytes = input_buffer_size;

    // The replacement tag starts with '$' and ends with '}'. The white-space
    // characters are an optimization that looks for characters that are invalid
    // within $i18n{} tags.
    size_t pos = input_.find_last_of("$} \t\r\n");
    std::string to_process;
    if (!upstream_end_reached && pos != std::string::npos && input_[pos] == '$') {
        // If there is a trailing '$' then split the |input_| at that point. Process
        // the first part; save the second part for the next call to FilterData().
        to_process.assign(input_, 0, pos);
        input_.erase(0, pos);
    } else {
        // There is no risk of a split key, process the whole input.
        to_process.swap(input_);
    }

    output_.append(ui::ReplaceTemplateExpressions(to_process, *replacements_));
    int bytes_out = std::min(output_.size(), static_cast<size_t>(output_buffer_size));
    output_.copy(output_buffer->data(), bytes_out);
    output_.erase(0, bytes_out);
    return bytes_out;
}

} // namespace content
